#!/usr/bin/perl -w
package clreqd_modules;

use strict;
use Data::Dumper;
use mnttab;
use cldb;
use lvm;
use clreq;
use clreqdxml;

#########################################################################
# This is the code that lha_buildapp --validate uses on each of machines#
# configured for the application get details of a specified volume 	#
# group. If the details are different then something is wrong!		#
#									#
# This query can only be run on a single node at a time since it 	#
# attempts to gain/release the VG exclusively as part of the test.	#
#									#
# 12.02.08 - It has been modified to ensure that the NFS functionality	#
#	     can be coped with. Further changes for other		#
#	     non-replicated storage specs might have to be added at	#
#	     a later date.						#
#########################################################################

sub lvm_attempt_use {
my %ARGS=@_;

	local $SIG{CHLD}='DEFAULT';
	my $APP=$ARGS{APPLICATION};
	$::REPL_MAP=$ARGS{REPL_MAP};
	my @all_nodes=(split(/,/,$ARGS{NODES}));
	my @N=grep {$_ ne hostname} (split(/,/,$ARGS{NODES}));
	my $s=$ARGS{STORAGE_SPEC};
	if(! -f "$::ENV{TRUECL_CODE}/lib/storage_spec/$s.pm") {
		return "ERROR - Storage spec '$s' not available.";
	}
	my $VG=$ARGS{VGNAME};
	#################################################################
	# Before we get any further validate that the repl_ip info	#
	# contains details for all nodes...				#
	#################################################################
	my @repl_split=split(/\s+/,$::REPL_MAP);
	my %node_repl=();
	for my $cnode (@all_nodes) {
		if(! grep {/^$cnode:/} @repl_split) {
			return "ERROR - No REPL_MAP entry for '$cnode'.";
		}
	}
	for my $cent (@repl_split) {
		my ($node,$info)=($cent =~ /^(.*):(.*)/);
		$node_repl{$node}={};
		next if $info eq "NONE";
		my @x=split(/,/,$info);
		for my $cent2 (@x) {
			my ($top,$ip)=($cent2 =~ /^(.*)=(.*)/);
			next if (!defined($top) || !defined($ip));
			$node_repl{$node}->{$top}=$ip;
		}
	}
	my $CLREQDXML;
	::msg(Dumper(%node_repl));
	::msg("N=" . join(",",@N) . ", NODES=$ARGS{NODES}");
	if(@N) {
		#########################################################
		# We always get new lvm data for this request.		#
		#########################################################
		$CLREQDXML=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
		if(!defined($CLREQDXML)) {
			return "ERROR - Unable to load Request Daemon configuration info.";
		};
		my $OTHERS=new clreq(hosts => [@N], clreqdxml => $CLREQDXML, logger => $::LOGGER, timeout => 60);

		#################################################################
		# Now we call the clreqd:					#
		# now check for the ss_run_before_start_on_non_current(		#
		#	application,vgname,storage_spec)			#
		#################################################################

		my $results=$OTHERS->run(cmd => ["ss_run_before_start_on_non_current","APPLICATION",$APP,"REPL_MAP",$::REPL_MAP]);
		::msg("results:\n" . Dumper($results));
		#################################################################
		# check results... - though shouldn't really need to.		#
		#################################################################
		for my $ccc (@N) {
			if(!exists($results->{$ccc}) || $results->{$ccc} =~ /^ERROR/) {
				return "ERROR - Node '$ccc' has returned:\n$results->{$ccc}";
			}
		}
	}
	eval "use storage_spec::$s;";
	my $STORAGE=eval "new storage_spec::$s;";
	if(!defined($STORAGE)) {
		return "ERROR - $@ returned from 'new storage_spec::$s;'";
	}
	my ($rc,$err)=$STORAGE->run_before_start_on_current($VG,2);
	if(!$rc) {
		return "ERROR - $err from run_before_start_on_current";
	}

	#################################################################
	# For replicated storage the VG is not going to be native ...	#
	# so just getting this far is fine - but need to understand	#
	# how to know... using the replicated routine.			#
	#################################################################
	
	my @R=();
	if($STORAGE->replicated() == 0) {

		#########################################################
		# No activation needed - but we should mount the 	#
		# file system if not already mounted, and umount if	#
		# if successful. If it was mounted then do nothing.	#
		#########################################################

		if($ARGS{VGNAME} eq "NFS") {

			#################################################
			# Work out the command to mount the file sys	#
			#################################################
			my $mount_cmd=undef;
			my $umount_cmd=undef;
			if(-x "/sbin/mount") {
				$mount_cmd="/sbin/mount ";
				$umount_cmd="/sbin/umount ";
			} else {
				$mount_cmd="/usr/sbin/mount ";
				$umount_cmd="/usr/sbin/umount ";
			}
			if($Config{'osname'} eq "linux") {
				$mount_cmd.="-t nfs ";
			} else {
				$mount_cmd.="-F nfs ";
			}

			my $mnt=new mnttab();
			#################################################################
			# Get a list of all NFS items since vgname will be nfs...	#
			#################################################################
			my $cldb=new cldb("$::ENV{TRUECL_CFG}/config.db");
			if(!defined($cldb)) {
				return(0,"Unable to connect to 'config' database.\nReason: " . (cldb::error)[1]);
			}

			#################################################################
			# Use config database to get all nfs stuff for application.	#
			#################################################################

			my $r=$cldb->get_rows("SELECT * FROM application_fs WHERE name = '$ARGS{APPLICATION}';");
			$cldb->disconnect;

			my @errors=();
			for my $crec (@$r) {
				next if !defined($crec->{lvname});
				my $mnt_pnt=$mnt->get_from_vglv("NFS",$crec->{lvname});
				if(defined($mnt_pnt) && $mnt_pnt->{FS} eq $crec->{mountpnt}) {
					next; # Everything fine...
				}
				if(defined($mnt_pnt)) {
					push @errors,"NFS device '$crec->{lvname}' used, but mounted as '$mnt_pnt->{FS}' - should be '$crec->{mountpnt}'.";
					next;
				}

				#########################################################
				# The device is not mounted, so we perform the mount 	#
				# now. 							#
				#########################################################
				my $xx="$mount_cmd $crec->{lvname} $crec->{mountpnt}";
				my $xx2="$umount_cmd $crec->{mountpnt}";

				system($xx);
				if($?>>8==0) {
					system($xx2);
					next; # mounted, and then un-mounted, so fine
				}
				#########################################################
				# Failed to mount, so generate error			#
				#########################################################
				push @errors,"Unable to mount '$crec->{lvname} => $crec->{mountpnt}'.";
			}
			if(@errors) {
				return join("ERROR: ",@errors);
			}
			return "OK";
		}

		#################################################################
		# At this point we should be able to get new LVM stuff and	#
		# return a list of logical volumes as requested.		#
		#################################################################

		my $LVM_STUFF=new lvm();
		if(!defined($LVM_STUFF)) {
			return "ERROR - Unable to determine LVM/VXVM environment!";
		}

		#################################################################
		# We need NOT need to active/deactivate the vg in question -	#
		# this is performed via the run_before_start_on_current and	#
		# run_before_shutdown_on_current - if necessary for the storage.#
		#################################################################

		my @K=$LVM_STUFF->lv_list(VGNAME=>$ARGS{VGNAME});
		if(!defined($K[0])) {
			return "WARNING No volumes configured for VG/DG '$ARGS{VGNAME}'.";
		}

		for (@K) {
			my @x=$LVM_STUFF->get_lvinfo(VGNAME=>$ARGS{VGNAME},LVNAME=>$_);
			if(@x) {
				push @R,"$_ $x[1] $x[0] $x[2]";
			}
		}
	}

	#################################################################
	# After getting the information export the volume group if poss	#
	#################################################################
	
	($rc,$err)=$STORAGE->run_before_shutdown_on_current($VG);
	if(!$rc) {
		$::LOGGER->msg("WARN","WARNING - $err from run_before_shutdown_on_current");
	}

	#################################################################
	# Run run_before_shutdown_on_non_current here...		#
	#################################################################

	my @redo=();
	if(@N) {
		#########################################################
		# We always get new lvm data for this request.		#
		#########################################################
		$CLREQDXML=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
		if(!defined($CLREQDXML)) {
			return "ERROR - Unable to load Request Daemon configuration info.";
		};
		my $OTHERS=new clreq(hosts => [@N], clreqdxml => $CLREQDXML, logger => $::LOGGER, timeout => 60);

		#################################################################
		# Now we call the clreqd:					#
		# now check for the ss_run_before_shutdown_on_non_current(	#
		#	application,vgname,storage_spec)			#
		#################################################################

		my $results=$OTHERS->run(cmd => ["ss_run_before_shutdown_on_non_current","APPLICATION",$APP,"REPL_MAP",$::REPL_MAP]);
		::msg("results:\n" . Dumper($results));
		for my $chost (keys(%$results)) {
			if($results->{$chost} =~ /^RETRY/) {
				push @redo,$chost;
			}
		}
	}

	#################################################################
	# After getting the information export the volume group if poss	#
	#################################################################
	
	# ($rc,$err)=$STORAGE->run_before_shutdown_on_current($VG);
	# if(!$rc) {
		# $::LOGGER->msg("WARN","WARNING - $err from run_before_shutdown_on_current");
	# }

	#################################################################
	# If we get this far we also need to check to see if any	#
	# resources need to be stopped again remotely...		#
	#################################################################

	if(@redo) {
		$CLREQDXML=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
		if(!defined($CLREQDXML)) {
			return "ERROR - Unable to load Request Daemon configuration info.";
		};
		my $OTHERS=new clreq(hosts => [@redo], clreqdxml => $CLREQDXML, logger => $::LOGGER, timeout => 60);

		#################################################################
		# Now we call the clreqd:					#
		# now check for the ss_run_before_shutdown_on_non_current(	#
		#	application,vgname,storage_spec)			#
		#################################################################

		my $results=$OTHERS->run(cmd => ["ss_run_before_shutdown_on_non_current","APPLICATION",$APP,"REPL_MAP",$::REPL_MAP]);
		::msg("results:\n" . Dumper($results));
		for my $chost (keys(%$results)) {
			if($results->{$chost} !~ /^OK/) {
				$::LOGGER->msg("WARN","WARNING - Got $results->{$chost} from $chost.");
			}
		}
	}
	if($STORAGE->replicated() == 1) {
		return "REPLICATED";
	}
	return join("\n",@R);
}

1;
